namespace TfsExtensions.Build.Helper
{
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Text;
    using System.Xml;
    using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
    using Microsoft.Practices.EnterpriseLibrary.Logging;
    using Microsoft.Practices.EnterpriseLibrary.Logging.Filters;
    using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters;
    using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;
    using System.Reflection;

    /// <summary>
    /// Logging Category
    /// </summary>
    public enum LoggingCategory
    {
        /// <summary>
        /// LoggingCategory Info
        /// </summary>
        Info, 

        /// <summary>
        /// LoggingCategory Trace
        /// </summary>
        Trace, 

        /// <summary>
        /// LoggingCategory Warn
        /// </summary>
        Warn, 

        /// <summary>
        /// LoggingCategory Error
        /// </summary>
        Error
    }

    /// <summary>
    /// Logging Class
    /// </summary>
    public static class LoggingHelper2
    {
        // Determines if Logging is turned on.

        /// <summary>
        /// The config file path.
        /// </summary>
        private static readonly string ConfigFilePath = Path.Combine(
            Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), 
            Constants.ConfigurationFileName);

        /// <summary>
        /// The is loggin on.
        /// </summary>
        private static readonly bool IsLogginOn = CheckIfLoggingIsEnabled();

        /// <summary>
        /// The log format.
        /// </summary>
        private static string logFormat = string.Empty;

        /// <summary>
        /// Logs the info.
        /// </summary>
        /// <param name="source">
        /// The source.
        /// </param>
        /// <param name="message">
        /// The message.
        /// </param>
        /// <param name="additionalInfo">
        /// The additional info.
        /// </param>
        public static void LogInfo(string source, string message, string additionalInfo)
        {
            if (IsLogginOn)
            {
                LogEntry logEntry = CreateLogEntry(source, message, LoggingCategory.Info, additionalInfo);
                LogWriter logwriter = GetLogWriter(LoggingCategory.Info);
                WriteLog(logwriter, logEntry);
            }
        }

        /// <summary>
        /// Logs the trace.
        /// </summary>
        /// <param name="source">
        /// The source.
        /// </param>
        /// <param name="message">
        /// The message.
        /// </param>
        /// <param name="additionalInfo">
        /// The additional info.
        /// </param>
        public static void LogTrace(string source, string message, string additionalInfo)
        {
            if (IsLogginOn)
            {
                LogEntry logEntry = CreateLogEntry(source, message, LoggingCategory.Trace, additionalInfo);
                LogWriter logwriter = GetLogWriter(LoggingCategory.Trace);
                WriteLog(logwriter, logEntry);
            }
        }

        /// <summary>
        /// Logs the warning.
        /// </summary>
        /// <param name="source">
        /// The source.
        /// </param>
        /// <param name="message">
        /// The message.
        /// </param>
        /// <param name="additionalInfo">
        /// The additional info.
        /// </param>
        public static void LogWarning(string source, string message, string additionalInfo)
        {
            if (IsLogginOn)
            {
                LogEntry logEntry = CreateLogEntry(source, message, LoggingCategory.Warn, additionalInfo);
                LogWriter logwriter = GetLogWriter(LoggingCategory.Warn);
                WriteLog(logwriter, logEntry);
            }
        }

        /// <summary>
        /// Checks if logging is enabled.
        /// </summary>
        /// <returns>
        /// The check if logging is enabled.
        /// </returns>
        public static bool CheckIfLoggingIsEnabled()
        {
            bool value =
                ParseHelper.ParseBoolean(ConfigurationHelper.ReadConfigSectionKeyValue(
                                             ConfigFilePath,
                                             Constants.LoggerSwitchSectionName,
                                             Constants.EnabledKeyName));
            return value;
        }

        /// <summary>
        /// Logs the error.
        /// </summary>
        /// <param name="source">
        /// The source.
        /// </param>
        /// <param name="message">
        /// The message.
        /// </param>
        /// <param name="additionalInfo">
        /// The additional info.
        /// </param>
        public static void LogError(string source, string message, string additionalInfo)
        {
            if (IsLogginOn)
            {
                source = "Error at : " + source;
                LogEntry logEntry = CreateLogEntry(source, message, LoggingCategory.Error, additionalInfo);
                LogWriter logwriter = GetLogWriter(LoggingCategory.Error);
                WriteLog(logwriter, logEntry);
            }
        }

        /// <summary>
        /// Creates the log entry.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="message">The message.</param>
        /// <param name="logCategory">The log category.</param>
        /// <param name="additonalInfo">The additonal info.</param>
        /// <returns>Log Entry object</returns>
        private static LogEntry CreateLogEntry(
            string source, 
            string message, 
            LoggingCategory logCategory, 
            string additonalInfo)
        {
            IDictionary<string, object> customProperties = null;
            ICollection<string> categories = new List<string>();

            categories.Add(logCategory.ToString());
            if (!string.IsNullOrEmpty(source) && !string.IsNullOrEmpty(additonalInfo))
            {
                customProperties = new Dictionary<string, object>();
                customProperties.Add(source, additonalInfo);
            }

            var logEntry = new LogEntry();
            logEntry.Categories = categories;
            logEntry.ExtendedProperties = customProperties;
            logEntry.Message = message;
            logEntry.TimeStamp = DateTime.Now;
            logEntry.Title = "Log message";
            logEntry.Severity = TraceEventType.Information;
            return logEntry;
        }

        /// <summary>
        /// Gets the log writer.
        /// </summary>
        /// <param name="logCategory">The log category.</param>
        /// <returns>Log Writer</returns>
        private static LogWriter GetLogWriter(LoggingCategory logCategory)
        {
            string formatterName = string.Empty;
            try
            {
                string logFilePath = ConfigurationHelper.GetConfiguredLogFilePath(
                    logCategory, 
                    ConfigFilePath, 
                    out formatterName);

                if (string.IsNullOrEmpty(logFilePath))
                {
                    var logWriterFactory = new LogWriterFactory(new FileConfigurationSource(ConfigFilePath));
                    return logWriterFactory.Create();
                }

                LogWriter logWriter = null;

                if (string.IsNullOrEmpty(formatterName))
                {
                    formatterName = "Text Formatter";
                }

                string formatNodeXPathQuery = string.Format(
                    CultureInfo.InvariantCulture, 
                    "configuration/loggingConfiguration/formatters/add[@name='{0}']", 
                    formatterName);
                if (string.IsNullOrEmpty(logFormat))
                {
                    var configFileXml = new XmlDocument();
                    configFileXml.Load(ConfigFilePath);
                    XmlNode formatterNode = configFileXml.SelectSingleNode(formatNodeXPathQuery);
                    if (formatterNode == null || formatterNode.Attributes["template"] == null ||
                        string.IsNullOrEmpty(formatterNode.Attributes["template"].Value.Trim()))
                    {
                        var logFormatBuilder = new StringBuilder();
                        logFormatBuilder.Append(
                            "____________________________________________________________________________________________________________ {newline}");
                        logFormatBuilder.Append(
                            "Timestamp: {timestamp};{newline} Message: {message};{newline} Category: {category};{newline}  Priority: {priority};");
                        logFormatBuilder.Append(
                            "{newline}  EventId: {eventid};{newline}  Severity: {severity};{newline}  Title:{title};{newline}  Machine: {machine};");
                        logFormatBuilder.Append(
                            "{newline}  Application Domain: {appDomain};{newline}  Process Id: {processId};{newline}  Process Name: {processName};");
                        logFormatBuilder.Append(
                            "{newline}  Win32 Thread Id: {win32ThreadId};{newline}  Thread Name: {threadName};");
                        logFormatBuilder.Append(
                            "{newline}  Extended Properties: {dictionary({key} - {value})} {newline}");
                        logFormatBuilder.Append(
                            "____________________________________________________________________________________________________________ {newline}");
                        logFormat = logFormatBuilder.ToString();
                    }
                    else
                    {
                        logFormat = formatterNode.Attributes["template"].Value.Trim();
                    }
                }

                // Trace listener
                var flatFileTraceListener = new FlatFileTraceListener(logFilePath, new TextFormatter(logFormat));

                // LogSource
                var logSource = new LogSource(logCategory.ToString());
                logSource.Listeners.Add(flatFileTraceListener);

                // UnUsed Log Source
                var unUsedLogSource = new LogSource("UnUsedLogSource");

                // Trace Sources
                var traceSources = new Dictionary<string, LogSource>();
                traceSources.Add(logCategory.ToString(), logSource);

                // LogFilters
                var logFilters = new List<ILogFilter>();
                logFilters.Add(new PriorityFilter("PriorityFilter", 2));
                var cateogryFilterTypes = new List<string>();
                cateogryFilterTypes.Add("UI Events");

                logFilters.Add(new CategoryFilter(
                                   "CategoryFilter", 
                                   cateogryFilterTypes, 
                                   CategoryFilterMode.AllowAllExceptDenied));

                logWriter = new LogWriter(
                    logFilters, 
                    traceSources, 
                    logSource, 
                    unUsedLogSource, 
                    unUsedLogSource, 
                    logCategory.ToString(), 
                    true, 
                    false);

                return logWriter;
            }
            catch (ConfigurationErrorsException cEx)
            {
                try
                {
                    string stackTrace = "Logging Enabled: " + IsLogginOn + "  \n Unable to create log writer instance: " +
                                        cEx.Message + "\n\n " + cEx.StackTrace;

                    EventLog.WriteEntry("GetLogWriter()", stackTrace);
                }
                catch (InvalidOperationException)
                {
                    // System.Windows.Forms.MessageBox.Show("Brd.VS2005 GetLogWriter(). Logging Enabled: " + IsLogginOn.ToString() + "  \n Unable to create log writer instance: " + cEx.Message + "\n\n " + cEx.StackTrace, "Error",System.Windows.Forms.MessageBoxButtons.OK);
                }

                return null;
            }
            catch (Exception ex)
            {
                try
                {
                    string stackTrace = "Logging Enabled: " + IsLogginOn + "  \n Unable to create log writer instance: " +
                                        ex.Message + "\n\n " + ex.StackTrace;
                    EventLog.WriteEntry("GetLogWriter()", stackTrace);
                }
                catch (InvalidOperationException)
                {
                    // System.Windows.Forms.MessageBox.Show("Brd.VS2005 GetLogWriter(). Logging Enabled: " + IsLogginOn.ToString() + "  \n Unable to create log writer instance: " + ex.Message + "\n\n " + ex.StackTrace, "Error", System.Windows.Forms.MessageBoxButtons.OK);
                }

                return null;
            }
        }

        /// <summary>
        /// Writes the log.
        /// </summary>
        /// <param name="logWriter">
        /// The log writer.
        /// </param>
        /// <param name="logEntry">
        /// The log entry.
        /// </param>
        private static void WriteLog(LogWriter logWriter, LogEntry logEntry)
        {
            if (logWriter != null && logEntry != null && logWriter.ShouldLog(logEntry))
            {
                logWriter.Write(logEntry);
                logWriter.Dispose();
            }
        }
    }
}